﻿using System;
using System.Linq.Expressions;

namespace MathService.MathParser.ExpressionTrees.Nodes
{
    /// <summary>Узел дерева мат.выражения, реазлиующий оператор равенства</summary>
    public class EqualityOperatorNode : LogicOperatorNode
    {
        /// <summary>Получить выражение вызова метода определения модуля числа из класса Math</summary>
        /// <param name="x">Параметр выражения</param>
        /// <returns>Выражение Linq.Expression, Вызывающее метод Math.Abs с параметром x</returns>
        public static Expression GetAbsMethodCall(Expression x)
        {
            return Expression.Call(((Func<double, double>)Math.Abs).Method, x);
        }

        /// <summary>Точность вычисления равенства для чисел с плавающей точкой</summary>
        internal static double sf_Epsilon = 1e-12;

        /// <summary>Точность вычисления равенства для чисел с плавающей точкой</summary>
        public static double Epsilon { get { return sf_Epsilon; } set { sf_Epsilon = value; } }

        /// <summary>Инициализация нового узла оператора равенства</summary>
        public EqualityOperatorNode() : base("=", -30) { }

        /// <summary>Инициализация нового узла оператора равенства</summary>
        /// <param name="Left">Левое поддерево выражения</param>
        /// <param name="Right">Правое поддерево выражения</param>
        public EqualityOperatorNode(ExpressionTreeNode Left, ExpressionTreeNode Right)
            : this()
        {
            this.Left = Left;
            this.Right = Right;
        }

        /// <summary>Метод сравнения</summary>
        /// <param name="x">Первое значение</param>
        /// <param name="y">Второе значение</param>
        /// <returns>0 - если разность между x и y по модулю меньше Epsilon и 1 во всех остальных случаях</returns>
        private static double Comparer(double x, double y) { return Math.Abs(x - y) < sf_Epsilon ? 1 : 0; }

        /// <summary>Вычисление значения узла</summary>
        /// <returns>0 - если разность между x и y по модулю меньше Epsilon и 1 во всех остальных случаях</returns>
        public override double Compute()
        {
            return Comparer(((ComputedNode)Left).Compute(), ((ComputedNode)Right).Compute());
        }

        /// <summary>Компиляция логики узла</summary>
        /// <returns>Скомпилированное логическое выражение, реализующее операцию сравнения Равенство</returns>
        public override Expression LogicCompile()
        {
            return Left is LogicOperatorNode && Right is LogicOperatorNode
                ? Expression.Equal(((LogicOperatorNode)Left).LogicCompile(), ((LogicOperatorNode)Right).LogicCompile())
                : Expression.LessThan
                    (
                        GetAbsMethodCall(Expression.Subtract
                        (
                            ((ComputedNode)Left).Compile(),
                            ((ComputedNode)Right).Compile())
                        ),
                        Expression.Constant(sf_Epsilon)
                    );
        }

        /// <summary>Компиляция логики узла</summary>
        /// <param name="Parameters">Параметры компиляции</param>
        /// <returns>Скомпилированное логическое выражение, реализующее операцию сравнения Равенство</returns>
        public override Expression LogicCompile(ParameterExpression[] Parameters)
        {
            return Left is LogicOperatorNode && Right is LogicOperatorNode
                ? Expression.Equal(((LogicOperatorNode)Left).LogicCompile(Parameters), ((LogicOperatorNode)Right).LogicCompile(Parameters))
                : Expression.LessThan
                    (
                        GetAbsMethodCall(Expression.Subtract
                        (
                            ((ComputedNode)Left).Compile(Parameters),
                            ((ComputedNode)Right).Compile(Parameters))
                        ),
                        Expression.Constant(sf_Epsilon)
                    );

        }

        /// <summary>Клонирование узла</summary>
        /// <returns>Клон узла</returns>
        public override ExpressionTreeNode Clone() { return CloneOperatorNode<EqualityOperatorNode>(); }
    }
}